/******************************************************************************
* @file    lcd_1289.c
* @version V1.0
* @brief   TFT Driver program
******************************************************************************/
/*****************************
**                          **
** STM32TFT_LCD(SSD1289)    **
** PE0~15 <----> DB0~15     **
** PD15   <----> nRD        **
** PD13   <----> RS         **
** PB14   <----> nWR        **
** PC8    <----> nCS        **
** Reset  <----> nReset     **
** VCC    <----> BK_LED     **
******************************/

#include "lcd_1289.h"
#include "lcd_font.h"
#include "stm32_hal_legacy.h"
#include "stm32f107xc.h"
#include "stm32f1xx_hal_gpio.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Private typedef -----------------------------------------------------------*/

/*******************************************************************************
* Function Name  : LCD_DB_AS_InPut
* Description    : config MCU LCD_DB pins AS InPut
* Input          : None
* Output         : None
* Return         : None.
*******************************************************************************/
void LCD_DB_AS_InPut(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  // DB15--0
  GPIO_InitStructure.Pin = GPIO_PIN_All;
  GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
}

/*******************************************************************************
* Function Name  : LCD_DB_AS_OutPut
* Description    : config MCU LCD_DB pins AS OutPut
* Input          : None
* Output         : None
* Return         : None.
*******************************************************************************/
void LCD_DB_AS_OutPut(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  // DB15--0
  GPIO_InitStructure.Pin = GPIO_PIN_All;
  GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
}
/*******************************************************************************
* Function Name  : LCD_Pins_Config
* Description    : Configures LCD Pins
Push-Pull mode.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void LCD_Pins_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  //	RCC_APB2PeriphClockCmd(LCD_CLK_RS | RCC_APB2Periph_GPIOE |
  //		LCD_CLK_WR | LCD_CLK_RD |
  //		LCD_CLK_CS, ENABLE);
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();

  // DB15--0
  GPIO_InitStructure.Pin = GPIO_PIN_All;
  GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);

  // LCD_Pin_WR
  GPIO_InitStructure.Pin = LCD_Pin_WR;
  HAL_GPIO_Init(LCD_PORT_WR, &GPIO_InitStructure);

  // LCD_Pin_CS
  GPIO_InitStructure.Pin = LCD_Pin_CS;
  HAL_GPIO_Init(LCD_PORT_CS, &GPIO_InitStructure);

  // LCD_Pin_RS
  GPIO_InitStructure.Pin = LCD_Pin_RS;
  HAL_GPIO_Init(LCD_PORT_RS, &GPIO_InitStructure);

  // LCD_Pin_RD
  GPIO_InitStructure.Pin = LCD_Pin_RD;
  HAL_GPIO_Init(LCD_PORT_RD, &GPIO_InitStructure);

  SetCs
}

void LCD_Write(uint16_t PortVal)
{
  /* Check the parameters */
  GPIOE->ODR = PortVal;
}

uint16_t LCD_Read()
{
  /* Check the parameters */

  return ((uint16_t)GPIOE->IDR);
}
/*******************************************************************************
* Function Name  : LCD_WriteReg
* Description    : Writes to the selected LCD register.
* Input          : - LCD_Reg: address of the selected register.
*                  - LCD_RegValue: value to write to the selected register.
* Output         : None
* Return         : None
* author         : www.armjishu.com
*******************************************************************************/
void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)
{
  /* Write 16-bit Index, then Write Reg */
  ClrCs ClrRs ClrWr LCD_Write(LCD_Reg);
  SetWr
      /* Write 16-bit Reg */
      SetRs ClrWr LCD_Write(LCD_RegValue);
  SetWr SetCs
}

/*******************************************************************************
* Function Name  : LCD_ReadReg
* Description    : Reads the selected LCD Register.
* Input          : None
* Output         : None
* Return         : LCD Register Value.
*******************************************************************************/
u16 LCD_ReadReg(u8 LCD_Reg)
{
  u16 data;

  /* Write 16-bit Index (then Read Reg) */
  ClrCs ClrRs ClrWr LCD_Write(LCD_Reg);
  SetWr

      /* Read 16-bit Reg */
      SetRs ClrRd SetRd data = LCD_Read();
  SetCs

      return data;
}

u16 LCD_ReadSta(void)
{
  u16 data;

  /* Write 16-bit Index, then Write Reg */
  SetRs ClrRd SetRd data = LCD_Read();
  SetCs

      return data;
}

void LCD_WriteCommand(u16 LCD_RegValue)
{
  /* Write 16-bit Index, then Write Reg */
  ClrCs ClrRs ClrWr LCD_Write(LCD_RegValue);
  SetWr SetCs
}

/*******************************************************************************
* Function Name  : LCD_WriteRAM_Prepare
* Description    : Prepare to write to the LCD RAM.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void LCD_WriteRAM_Prepare(void)
{
  /* Write 16-bit Index, then Write Reg */
  ClrCs ClrRs ClrWr LCD_Write(R34);
  SetWr SetCs
}

/*******************************************************************************
* Function Name  : LCD_WriteRAM
* Description    : Writes to the LCD RAM.
* Input          : - RGB_Code: the pixel color in RGB mode (5-6-5).
* Output         : None
* Return         : None
*******************************************************************************/
void LCD_WriteRAM(u16 RGB_Code)
{
  /* Write 16-bit Index, then Write Reg */
  ClrCs SetRs ClrWr LCD_Write(RGB_Code);
  SetWr SetCs
}

/*******************************************************************************
* Function Name  : LCD_ReadRAM
* Description    : Reads the LCD RAM.
* Input          : None
* Output         : None
* Return         : LCD RAM Value.
*******************************************************************************/
u16 LCD_ReadRAM(void)
{
  u16 dummy;
  u16 data;
  LCD_WriteRAM_Prepare();
  LCD_DB_AS_InPut();
  dummy = LCD_ReadSta();
  dummy++;
  data = LCD_ReadSta();
  LCD_DB_AS_OutPut();

  return data;
}

void Delay(__IO u32 nCount)
{
  __IO u32 TimingDelay;
  while (nCount--)
  {
    for (TimingDelay = 0; TimingDelay < 1000; TimingDelay++)
      ;
  }
}

/****************************************************************************
* 名    称：void LCD_Initializtion()
* 功    能：初始化 SSD1289控制器
* 入口参数：无
* 出口参数：无
* 说    明：
* 调用方法：LCD_Initializtion();
****************************************************************************/
void LCD_Initializtion(void)
{
  u16 DeviceIdCode;
  LCD_Pins_Config(); /* 配置管脚 Configure the LCD Control pins */
  Delay(5);          /* delay 50 ms */
  LCD_WriteReg(0x0000, 0x0001);
  Delay(5); /* delay 50 ms */ // start internal osc
  LCD_DB_AS_InPut();
  Delay(1); /* delay 50 ms */ // start internal osc
  DeviceIdCode = LCD_ReadReg(0x0000);
  printf("\n\r ###### www.armjishu.com DeviceIdCode = 0x%x ###### ",
         DeviceIdCode);
  LCD_DB_AS_OutPut();
  Delay(1); /* delay 50 ms */ // start internal osc
  if (DeviceIdCode == 0x8989)
  {
    printf("\n\r This LCD is SSD1289 ");
    LCD_WriteReg(0x0000, 0x0001); //打开晶振
    LCD_WriteReg(0x0003, 0xA8A4); // 0xA8A4
    LCD_WriteReg(0x000C, 0x0000);
    LCD_WriteReg(0x000D, 0x080C);
    LCD_WriteReg(0x000E, 0x2B00);
    LCD_WriteReg(0x001E, 0x00B0);
    LCD_WriteReg(0x0001, 0x6B3F); //驱动输出控制320*240  0x693F  0x2B3F
    LCD_WriteReg(0x0002, 0x0600); // LCD Driving Waveform control
    LCD_WriteReg(0x0010, 0x0000);
    LCD_WriteReg(0x0011,
                 0x6078); // 0x4030	//定义数据格式  16位色	横屏 0x6058
    LCD_WriteReg(0x0005, 0x0000);
    LCD_WriteReg(0x0006, 0x0000);
    LCD_WriteReg(0x0016, 0xEF1C);
    LCD_WriteReg(0x0017, 0x0003);
    LCD_WriteReg(0x0007, 0x0233); // 0x0233
    LCD_WriteReg(0x000B, 0x0000);
    LCD_WriteReg(0x000F, 0x0000); //扫描开始地址
    LCD_WriteReg(0x0041, 0x0000);
    LCD_WriteReg(0x0042, 0x0000);
    LCD_WriteReg(0x0048, 0x0000);
    LCD_WriteReg(0x0049, 0x013F);
    LCD_WriteReg(0x004A, 0x0000);
    LCD_WriteReg(0x004B, 0x0000);
    LCD_WriteReg(0x0044, 0xEF00);
    LCD_WriteReg(0x0045, 0x0000);
    LCD_WriteReg(0x0046, 0x013F);
    LCD_WriteReg(0x0030, 0x0707);
    LCD_WriteReg(0x0031, 0x0204);
    LCD_WriteReg(0x0032, 0x0204);
    LCD_WriteReg(0x0033, 0x0502);
    LCD_WriteReg(0x0034, 0x0507);
    LCD_WriteReg(0x0035, 0x0204);
    LCD_WriteReg(0x0036, 0x0204);
    LCD_WriteReg(0x0037, 0x0502);
    LCD_WriteReg(0x003A, 0x0302);
    LCD_WriteReg(0x003B, 0x0302);
    LCD_WriteReg(0x0023, 0x0000);
    LCD_WriteReg(0x0024, 0x0000);
    LCD_WriteReg(0x0025, 0x8000);
    LCD_WriteReg(0x004e, 0); //列(X)首址0
    LCD_WriteReg(0x004f, 0); //行(Y)首址0
  }
  else
  {
    printf("\n\r ###### Err: Is Not SD1289 DeviceIdCode 0x%x ###### ",
           DeviceIdCode);
  }

  Delay(2000);

  Lcd_Clear(Red);
}

/****************************************************************************
* 名    称：void Lcd_SetCursor(u16 x,u16 y)
* 功    能：设置屏幕座标
* 入口参数：x      行座标
*           y      列座标
* 出口参数：无
* 说    明：
* 调用方法：Lcd_SetCursor(10,10);
****************************************************************************/
void Lcd_SetCursor(u8 x, u16 y)
{
  LCD_WriteReg(0x004e, x); //行
  LCD_WriteReg(0x004f, y); //列
}

/****************************************************************************
* 名    称：Lcd_SetWindows(u8 StartX,u16 StartY,u8 EndX,u16 EndY)
* 功    能：设置屏幕窗口范围
* 入口参数：StartX     起始行座标
*           StartY     起始列座标
*           EndX       末始行座标
*           EndY       末始列座标
* 出口参数：无
* 说    明：
* 调用方法：Lcd_SetWindows(10,10，200,200);
****************************************************************************/
void Lcd_SetWindows(u8 StartX, u16 StartY, u8 EndX, u16 EndY)
{
  Lcd_SetCursor(StartX, StartY);
  LCD_WriteReg(0x0044, StartX | EndX << 8);
  LCD_WriteReg(0x0045, StartY);
  LCD_WriteReg(0x0046, EndY);
}

/****************************************************************************
* 名    称：void Lcd_SetPoint(u16 x,u16 y,u16 point)
* 功    能：在指定座标画点
* 入口参数：x      行座标
*           y      列座标
*           point  点的颜色
* 出口参数：无
* 说    明：
* 调用方法：Lcd_SetPoint(10,10,0x0fe0);
****************************************************************************/
void Lcd_SetPoint(u16 x, u16 y, u16 point)
{
  if ((x > 240) || (y > 320))
    return;
  Lcd_SetCursor(x, y);
  LCD_WriteRAM_Prepare();
  LCD_WriteRAM(point);
}

void Lcd_Clear(u16 Color)
{
  int i;
  Lcd_SetWindows(0, 0, 239, 319);
  LCD_WriteRAM_Prepare();
  for (i = 0; i < 240 * 320; i++)
  {
    LCD_WriteRAM(Color);
  }
}

void Draw_Line____(u8 x, u8 y, u8 Len, u16 point, u8 Dir)
{
  int i;
  for (i = 0; i < Len; i++)
  {
    if (!Dir)
    {
      if (x + i > 240)
      {
        return;
      }
      Lcd_SetPoint(x + i, y, point);
    }
    else
    {
      if (y + i > 320)
      {
        return;
      }
      Lcd_SetPoint(x, y + i, point);
    }
  }
}

void Draw_Bar(int xPos, int yPos, int Length, int width, int BkPoint,
              int ISBK)
{
  int i, j;
  if (!ISBK)
  {
    Draw_Line(xPos, yPos, Length, BkPoint, 1);
    Draw_Line(xPos, yPos, width, BkPoint, 0);
    Draw_Line(xPos + width, yPos, Length, BkPoint, 1);
    Draw_Line(xPos, yPos + Length, width, BkPoint, 0);
  }
  else
  {
    for (i = 0; i < Length; i++)
    {
      for (j = 0; j < width; j++)
      {
        Lcd_SetPoint(xPos + i, yPos + j, BkPoint);
      }
    }
  }
}

void Swap_Int(int *a, int *b)
{
  *a ^= *b;
  *b ^= *a;
  *a ^= *b;
}

// Bresenham's line algorithm
/**
  *    @name   :  Draw_Line
  *    @brief  :  ????
  *    @param  : x1,y1:?????,x2,y2:?????  color:??
  *    @retval :  None
  *    @author : NQ
  *    @date   :  2016-04-01
  */
void Draw_Line(int x1, int y1, int x2, int y2, u16 Color)
{
  int ix, iy, cx, n2dy, n2dydx, d, cy;
  int dx = abs(x2 - x1), dy = abs(y2 - y1), yy = 0;

  if (dx < dy)
  {
    yy = 1;
    Swap_Int(&x1, &y1);
    Swap_Int(&x2, &y2);
    Swap_Int(&dx, &dy);
  }

  ix = (x2 - x1) > 0 ? 1 : -1, iy = (y2 - y1) > 0 ? 1 : -1, cx = x1, cy = y1,
  n2dy = dy * 2, n2dydx = (dy - dx) * 2, d = dy * 2 - dx;

  if (yy)
  {
    while (cx != x2)
    {
      if (d < 0)
      {
        d += n2dy;
      }
      else
      {
        cy += iy;
        d += n2dydx;
      }
      Lcd_SetPoint(cy, cx, Color);
      cx += ix;
    }
  }
  else
  {
    while (cx != x2)
    {
      if (d < 0)
      {
        d += n2dy;
      }
      else
      {
        cy += iy;
        d += n2dydx;
      }
      Lcd_SetPoint(cx, cy, Color);
      cx += ix;
    }
  }
}

void _Draw_Circle_8(int xc, int yc, int x, int y, u16 Color)
{
  Lcd_SetPoint(xc + x, yc + y, Color);
  Lcd_SetPoint(xc - x, yc + y, Color);
  Lcd_SetPoint(xc + x, yc - y, Color);
  Lcd_SetPoint(xc - x, yc - y, Color);
  Lcd_SetPoint(xc + y, yc + x, Color);
  Lcd_SetPoint(xc - y, yc + x, Color);
  Lcd_SetPoint(xc + y, yc - x, Color);
  Lcd_SetPoint(xc - y, yc - x, Color);
}

/**
  *    @name   :  Draw_Circle
  *    @brief  :  ????
  *    @param  : xc,yc:????  r:??  color:??  fill:0-???  1-??
  *    @retval :  None
  *    @author : NQ
  *    @date   :  2016-04-01
  */
// Bresenham's circle algorithm
void Draw_Circle(int xc, int yc, int r, int fill, u16 Color)
{
  int x = 0, y = r, yi, d;
  d = 3 - 2 * r;

  if (fill)
  {
    while (x <= y)
    {
      for (yi = x; yi <= y; yi++)
        _Draw_Circle_8(xc, yc, x, yi, Color);

      if (d < 0)
      {
        d = d + 4 * x + 6;
      }
      else
      {
        d = d + 4 * (x - y) + 10;
        y--;
      }
      x++;
    }
  }
  else
  {
    while (x <= y)
    {
      _Draw_Circle_8(xc, yc, x, y, Color);

      if (d < 0)
      {
        d = d + 4 * x + 6;
      }
      else
      {
        d = d + 4 * (x - y) + 10;
        y--;
      }
      x++;
    }
  }
}

#define pi 3.1415

/**
  *    @name   :  GUI_AngelLine
  *    @brief  :  ????
  *    @param  : x0,y0:????  angel:??(0-360?)   length1:????  color:??
  *    @retval :  None
  *    @author : NQ
  *    @date   :  2016-04-01
  */
void Draw_AngelLine(u16 x0, u16 y0, u16 angel, u16 length1, u16 color)
{
  float d;
  float k;
  float length;
  u16 x, y;
  double sita;
  x = x0;
  y = y0;
  length = (float)length1;

  if (angel == 0 || angel == 90 || angel == 180 || angel == 270 ||
      angel == 360)
  {
    switch (angel)
    {
    case 0:
    {
      Draw_Line(x0, y0, x0 - length, y0, color);
    }
    break;
    case 90:
    {
      Draw_Line(x0, y0, x0, y0 + length, color);
    }
    break;
    case 180:
    {
      Draw_Line(x0, y0, x0 + length, y0, color);
    }
    break;
    case 270:
    {
      Draw_Line(x0, y0, x0, y0 - length, color);
    }
    break;
    case 360:
    {
      Draw_Line(x0, y0, x0 - length, y0, color);
    }
    break;
    }
  }

  if (angel > 0 && angel < 90)
  {
    sita = (90 - angel + 90);
    if (sita >= 360)
    {
      sita -= 360;
    }
    sita = sita / 180 * pi;
    k = tan(sita);
    if (k >= tan(3 * pi / 4))
    {
      d = 1.0 / 2 + k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          y++;
          x--;
          d = d + 1.0 + k;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          x--;
          d = d + k;
        }
      }
    }
    else
    {
      d = 1.0 + 1.0 / 2 * k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          y++;
          d = d + 1.0;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          x--;
          y++;
          d = d + k + 1.0;
        }
      }
    }
  }
  else if (angel > 90 && angel < 180)
  {
    sita = (90 - angel + 90);
    if (sita >= 360)
    {
      sita -= 360;
    }
    sita = sita / 180 * pi;
    k = tan(sita);
    if (k <= tan(pi / 4))
    {
      d = 1.0 / 2 - k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          x++;
          y++;
          d = d + 1.0 - k;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          x++;
          d = d - k;
        }
      }
    }
    else
    {
      d = 1.0 - 1.0 / 2 * k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          y++;
          d = d + 1.0;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          x++;
          y++;
          d = d - k + 1.0;
        }
      }
    }
  }
  else if (angel > 270 && angel < 360)
  {
    sita = (90 - angel + 90);
    if (sita >= 360)
    {
      sita -= 360;
    }
    sita = sita / 180 * pi;
    k = tan(sita);
    if (k <= tan(pi / 4))
    {
      d = (-1.0) / 2 + k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          x--;
          d = d + k;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          x--;
          y--;
          d = d - 1.0 + k;
        }
      }
    }
    else
    {
      d = (-1.0) + 1.0 / 2 * k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          x--;
          y--;
          d = d + k - 1.0;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          y--;
          d = d - 1.0;
        }
      }
    }
  }
  else if (angel > 180 && angel < 270)
  {
    sita = (90 - angel + 90);
    if (sita >= 360)
    {
      sita -= 360;
    }
    sita = sita / 180 * pi;
    k = tan(sita);
    if (k >= tan(3 * pi / 4))
    {
      d = (-1.0) / 2 - k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          x++;
          d = d - k;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          x++;
          y--;
          d = d - k - 1.0;
        }
      }
    }
    else
    {
      d = (-1.0) - 1.0 / 2 * k;
      while ((x - x0) * (x - x0) + (y - y0) * (y - y0) <= length * length)
      {
        if (d < 0)
        {
          Lcd_SetPoint(x, y, color);
          x++;
          y--;
          d = d - k - 1.0;
        }
        else
        {
          Lcd_SetPoint(x, y, color);
          y--;
          d = d - 1.0;
        }
      }
    }
  }
}

void Draw_Bar2(int xPos, int yPos, int Length, int width, int BkPoint,
               int ISBK)
{
  int i, j;
  if (!ISBK)
  {
    if (Length > 239 || width > 319)
    {
      return;
    }
    for (i = xPos; i < xPos + Length; i++)
    {
      Lcd_SetPoint(i, yPos, BkPoint);
      Lcd_SetPoint(i, yPos + width, BkPoint);
    }
    for (i = yPos; i < yPos + width; i++)
    {
      Lcd_SetPoint(xPos, i, BkPoint);
      Lcd_SetPoint(xPos + Length, i, BkPoint);
    }
  }
  else
  {
    if (Length > 239 || width > 319)
    {
      return;
    }
    for (j = yPos; j < yPos + width; j++)
    {
      for (i = xPos; i < xPos + Length; i++)
      {
        Lcd_SetPoint(i, j, BkPoint);
      }
    }
  }
}
